/*-
 * Copyright 2020 chenkang
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */



#include "masterthread.h"
#include "lzzip.h"
#include "bsdiff.h"
#include <QFile>
#include <QFileInfo>
#include <QDataStream>
#include <QDebug>

MasterThread::MasterThread(QObject *parent)
    : QThread(parent), quit(false)
{
}

//! [0]
MasterThread::~MasterThread()
{
    mutex.lock();
    quit = true;
    cond.wakeOne();
    mutex.unlock();
    wait();
}
//! [0]

//! [1] //! [2]
void MasterThread::transaction(const QString &OldFile_Name, const QString &NewFile_Name,  \
                               const QString &DiffFile_Dir, int Rom_Index)
{
    //! [1]
    QMutexLocker locker(&mutex);
    this->OldFile_Name = OldFile_Name;
    this->NewFile_Name = NewFile_Name;
    this->DiffFile_Dir = DiffFile_Dir;
    this->Rom_Index = Rom_Index;

    //! [3]
    if (!isRunning())
        start();
    else
        cond.wakeOne();
}
//! [2] //! [3]

//! [4]
void MasterThread::run()
{
    while(!quit){
        QFile Oldfile(OldFile_Name);
        QFile Newfile(NewFile_Name);
        if ((Oldfile.open(QIODevice::ReadOnly)) && (Newfile.open(QIODevice::ReadOnly))) {


            QFileInfo Oldfile_info(OldFile_Name);

            QFile Diff_file(DiffFile_Dir + "/" + Oldfile_info.baseName() + tr(".patch"));
            if(Diff_file.open((QIODevice::WriteOnly))){

                QDataStream Oldin(&Oldfile);
                QDataStream Newin(&Newfile);
                QDataStream Diffout(&Diff_file);
                off_t oldsize = Oldfile.size();
                off_t newsize = Newfile.size();

                uint8_t *old_data = new uint8_t[oldsize];
                uint8_t *new_data = new uint8_t[newsize];

                if((Oldin.readRawData((char *)old_data, oldsize) == -1) ||
                        Newin.readRawData((char *)new_data, newsize) == -1){
                    error(tr("read error"));
                }
                else{
                    off_t patch_size, After_Zip_data_size;
                    After_Zip_data_size = patch_size = newsize * 2;
                    uint8_t *patch_data = new uint8_t[patch_size];
                    uint8_t *After_Zip_data = new uint8_t[After_Zip_data_size];


                    bsdiff(this, old_data, oldsize, new_data, newsize, patch_data, &patch_size);
//                    qDebug() << "patch_size" << patch_size;
                    LZzip(this, patch_data, patch_size, newsize, After_Zip_data, &After_Zip_data_size, Rom_Index);
                    if(Diffout.writeRawData((const char *)After_Zip_data, After_Zip_data_size) == -1){
                        error(tr("write error"));
                    }
                    else{
                        response(tr("%1").\
                                 arg(DiffFile_Dir + "/" + Oldfile_info.baseName() + tr(".patch")));
                    }
                    delete []After_Zip_data;
                    delete []patch_data;
                }
                delete []old_data;
                delete []new_data;
//                qDebug() << "complete";
            }
            else{
                error(tr("open file failed"));
            }
            Diff_file.close();
        }
        else{
            error(tr("open file failed"));
        }
        Oldfile.close();
        Newfile.close();
        mutex.lock();
        cond.wait(&mutex);
        mutex.unlock();

    }
}
